﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using System.Linq;

namespace Stock.Core.MongoDB
{
    public interface IMongoService
    {
        MongoBaseRepository<T> Reposityory<T>() where T : BaseMongoEntity; 
    }

    public class MongoService:IMongoService
    {
        private readonly IOptions<MongoOptions> _opt;

        public MongoService(IOptions<MongoOptions> opt)
        {
            _opt = opt;
        }

        public MongoBaseRepository<T> Reposityory<T>() where T:BaseMongoEntity
        {
             
            return new MongoBaseRepository<T>(_opt);
        }
    }

    public interface IMongoBaseRepository<T>
    {
        IEnumerable<T> GetList();
        IMongoQueryable<T> GetQueryable();
        //List<T> GetList(Expression<Func<T, bool>> query);
        T Get(ObjectId id);
        Task Create(T p);
        Task CreateMany(List<T> p);
        Task<DeleteResult> Remove(ObjectId id);
        Task<UpdateResult> Update(ObjectId Id, Expression<Func<T, IEnumerable<object>>> filed, object value);
        Task<ReplaceOneResult> Update(ObjectId Id, T value);
        Task<DeleteResult> RemoveAll();
    }

    public class MongoBaseRepository<T> :IMongoBaseRepository<T> where T: BaseMongoEntity
    {
		MongoClient _client;
		IMongoDatabase _db;
        private string _collect { get; set; }
        MongoOptions _opt;
        
        public MongoBaseRepository(IOptions<MongoOptions> opt)
        {
            _opt = opt.Value;
            var mongoUrl = new MongoUrl(_opt.url);
            var clientSettings = MongoClientSettings.FromUrl(mongoUrl); 
            _client = new MongoClient(clientSettings);
            //_client = new MongoClient(_opt.url);
            _db = _client.GetDatabase(_opt.database);// GetServer();
            _collect = typeof(T).Name;

		}

        public IMongoQueryable<T> GetQueryable()
        {
            return _db.GetCollection<T>(_collect).AsQueryable();
        }

		public IEnumerable<T> GetList()
		{
            return _db.GetCollection<T>(_collect).Find(FilterDefinition<T>.Empty).ToList();
            //_db.GetCollection<T>().AsQueryable<T>().Where(a=>a.Id == "");
		}


		public T Get(ObjectId id)
		{
            var res = Builders<T>.Filter.Eq(e => e.BsonId, id); ;//  Query<T>.EQ(p => p.Id, id);
			return _db.GetCollection<T>(_collect).Find(res).Single();

		}

        public async Task Create(T p)
		{
            await _db.GetCollection<T>(_collect).InsertOneAsync(p);
		}

		public async Task CreateMany(List<T> p)
		{
            await _db.GetCollection<T>(_collect).InsertManyAsync(p);
		}

        public async Task<DeleteResult> Remove(ObjectId id)
		{
			var res = Builders<T>.Filter.Eq(e => e.BsonId, id);
            var operation = await _db.GetCollection<T>(_collect).DeleteOneAsync(res);
            return operation;
		}

		public async Task<DeleteResult> RemoveAll()
		{
            var operation = await _db.GetCollection<T>(_collect).DeleteManyAsync(FilterDefinition<T>.Empty);
			return operation;
		}

        public async Task<UpdateResult> Update(ObjectId Id, Expression<Func<T, IEnumerable<object>>> filed, object value)
		{
			var filter = Builders<T>.Filter.Eq(e => e.BsonId, Id);
            var updateResult = await _db.GetCollection<T>(_collect).UpdateOneAsync(filter, Builders<T>.Update.Push(filed, value));
            //Work with updateResult 
            return updateResult;
		}
        public async Task<ReplaceOneResult> Update(ObjectId Id, T value)
		{
			var filter = Builders<T>.Filter.Eq(e => e.BsonId, Id);
            var updateResult = await _db.GetCollection<T>(_collect).ReplaceOneAsync(filter, value);
			//Work with updateResult 
			return updateResult;
		}
    }
}
